/*
	setup_PIC_64.c

	contains functionality for setting up the PIC

	Author: Aidan Goddard, 14/5/13
*/

#include "../kernel/headers/types.h"

// some defines
#define PIC1		0x20
#define PIC2		0xa0
#define PIC1_COMMAND	PIC1
#define PIC2_COMMAND	PIC2
#define PIC1_DATA	(PIC1 + 1)
#define PIC2_DATA	(PIC2 + 1)

// initialise the PIC
void PIC_init(int start_vector)
{
	// save current masks in PICs
	unsigned char mask1, mask2;
	mask1 = inportb(PIC1_DATA);
	mask2 = inportb(PIC2_DATA);

	// send init command to both pics
	outportb(PIC1_COMMAND, 0x11);
	io_wait();
	outportb(PIC2_COMMAND, 0x11);
	io_wait();

	// send vector offsets to both pics
	outportb(PIC1_DATA, start_vector);		// master pic offset vector
	io_wait();
	outportb(PIC2_DATA, (start_vector + 8));	// secondary pic offset vector
	io_wait();

	// send master/slave config
	outportb(PIC1_DATA, 4);		// tell master the slave is at IRQ2
	io_wait();
	outportb(PIC2_DATA, 2);		// tell slave its cascade ID
	io_wait();

	// send environment info
	outportb(PIC1_DATA, 0x01);
	io_wait();
	outportb(PIC2_DATA, 0x01);
	io_wait();

	// restore saved masks
	outportb(PIC1_DATA, mask1);
	io_wait();
	outportb(PIC2_DATA, mask2);
	io_wait();

	return;
}

// mask / unmask the pic
void PIC_mask_IRQ(unsigned char IRQ)
{
	unsigned short port;
	unsigned char value;

	// check which controller to send the mask to
	if(IRQ > 7)
	{
		// secondary controller
		port = PIC2_DATA;
		IRQ = IRQ - 8;
	}
	else
	{
		// master controller;
		port = PIC1_DATA;
	}

	// get the current mask and set the correct bit in it
	value = inportb(port) | (1 << IRQ);

	// send new value to port
	outportb(port, value);

	return;
}

void PIC_unmask_IRQ(unsigned char IRQ)
{
	unsigned short port;
	unsigned char value;

	// check which controller to send the mask to
	if(IRQ > 7)
	{
		// secondary controller
		port = PIC2_DATA;
		IRQ = IRQ - 8;
	}
	else
	{
		// master controller;
		port = PIC1_DATA;
	}

	// get the current mask and clear the correct bit in it
	value = inportb(port) & ~(1 << IRQ);

	// send new value to port
	outportb(port, value);

	return;
}


// setup the PIC
void PIC_Setup(int start_vector)
{
	// initialise controller
	PIC_init(start_vector);

	// mask all
	int i;
	for(i = 0; i < 16; i++)
	{
		PIC_mask_IRQ(i);
	}

	return;
}


// disable the PIC
void PIC_Disable()
{
	// mask all interrupts
	__asm__ __volatile__("mov $0xff, %%al; outb %%al, $0xa1; outb %%al, $0x21":::"al", "memory");

	return;
}
